package com.wave.gate.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.norco.API.NorcoAPI;
import com.norco.API.callback.UartReceiveDataCallback;
import com.norco.API.peripheral.uart.ReceiveDataBean;
import com.norco.API.util.ByteUtil;
import com.wave.gate.constant.CommonConstants;
import com.wave.gate.enums.ConveyerEnum;
import com.wave.gate.enums.RedisKeyEnum;
import com.wave.gate.init.InitBaseInfo;
import com.wave.gate.netty.FaceServerHandler;
import com.wave.gate.vo.GatePassSetVo;
import com.wave.gate.vo.MdmMobileVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.wave.gate.util.GuavaCache.DOOR_MODE_CACHE;

/**
 * @author TanLei
 * @className：Test
 * @description：TODO
 * @date 2023/4/24 16:34
 * @version: 1.0
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "system.linux",havingValue = "true")
public class NorcoAPIUtil {

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private static NorcoAPIUtil instance;

    private static double weightTime;//电子秤称重的执行时间

    private static double electronicBalanceIndex;//使用的是哪个电子秤

    @Value("${electronicBalance.weight.time}")
    public double weightTime_temp;

    private static long weightError;//电子秤称重误差(g)

    @Value("${electronicBalance.weight.error}")
    public long weightError_temp;

    @Value("${electronicBalance.index}")
    public int electronicBalanceIndex_temp;

    public static NorcoAPI mNorcoAPI  = NorcoAPI.norcoAPICreate();

    public static volatile String mobileCode = "";//手机二维码

    public static volatile String mobileCode2 = "";//手机二维码2

    public static volatile String tokenCode = "";//token二维码

    public static volatile String tokenCode2 = "";//token二维码2

    public static Set<Double> weightSet1 = new LinkedHashSet<>();//电子秤重量,单位为克(g)

    public static Set<Double> weightSet2 = new LinkedHashSet<>();//电子秤重量,单位为克(g)

    public static volatile String weightZero = "";//电子秤置零/零点标定指令返回结果

    //复位键按钮（闸机、人脸机、传送带、电子秤复位）
    int gpioNumber_reset = 122;

    //1s闸机常闭、核验模式切换、5秒以上取物品
    int gpioNumber_door = 123;

    //电子门
    public static int gpioNumber_metal_detect = 121;

    //尾随入口
    int gpio_tailgating_in = 124;

    //尾随出口
    int gpio_tailgating_out = 125;

    public int gateAPort = 502;

    //B门闸机端口
    public int gateBPort = 502;

    /**
     * 00：常开；11：进向自由、出向自由
     */
    private final String doorCommand1 = "000000000006010600410011";

    /**
     * 01：核验；22：进向禁止、出向禁止
     */
    private final String doorCommand3 = "000000000006010600410122";

    public static Map<String, Integer> gpio121Map = new HashMap<>();

    //尾随红外
    public static Map<String, Integer> gpiohwMap = new HashMap<>();

    /**
     *
     * 开启扫码和电子秤的监听功能
     */
    @PostConstruct
    private void initNorcoAPI() {

        instance = this;
        instance.threadPoolTaskExecutor = this.threadPoolTaskExecutor;
        weightTime = weightTime_temp * 1000;
        weightError = weightError_temp;
        electronicBalanceIndex = electronicBalanceIndex_temp;
        log.info("手机扫码功能开启开始.....");

        mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE1, 57600, 0, 8, 1, 'N', 0, 1);
        mNorcoAPI.hwctrl_uartReceiveEndBytes(CommonConstants.COM_NODE1, "232323");//目前扫描仪一帧数据结束符为:###，井号对应的16进制ASCII码为32
        mNorcoAPI.hwctrl_uartReceive(CommonConstants.COM_NODE1, mDataCallbackScanCode);

        mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE3, 57600, 0, 8, 1, 'N', 0, 1);
        mNorcoAPI.hwctrl_uartReceiveEndBytes(CommonConstants.COM_NODE3, "232323");
        mNorcoAPI.hwctrl_uartReceive(CommonConstants.COM_NODE3, mDataCallbackScanCode);
        log.info("手机扫码功能开启结束.....");


        log.info("token扫码功能开启.....");

        mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE2, 57600, 0, 8, 1, 'N', 0, 1);
        mNorcoAPI.hwctrl_uartReceiveEndBytes(CommonConstants.COM_NODE2, "232323");
        mNorcoAPI.hwctrl_uartReceive(CommonConstants.COM_NODE2, mDataCallbackScanCode);

        mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE4, 57600, 0, 8, 1, 'N', 0, 1);
        mNorcoAPI.hwctrl_uartReceiveEndBytes(CommonConstants.COM_NODE4, "232323");
        mNorcoAPI.hwctrl_uartReceive(CommonConstants.COM_NODE4, mDataCallbackScanCode);

        log.info("token扫码功能开启结束.....");


        log.info("电子秤称重开启开始.....");

        //老称（暂时没用了）
        if(electronicBalanceIndex==1){
            log.info("电子秤1称重开启中.....");
            mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE0, 38400, 0, 8, 1, 'N', 0, 1);//
        }
        if(electronicBalanceIndex==2){
            log.info("电子秤2称重开启中.....");
            mNorcoAPI.hwctrl_uartOpen(CommonConstants.COM_NODE0, 9600, 0, 8, 1, 'E', 0, 1);//
        }
        mNorcoAPI.hwctrl_uartReceive(CommonConstants.COM_NODE0, mDataCallbackElectronicBalance);

        log.info("电子秤称重开启结束.....");

        //电子秤1置零
        /*NorcoAPIUtil.electronicBalanceSendData(CommonConstants.COM_NODE0, CommonConstants.ZEROORDER1);
        log.info("项目启动电子秤1置零结果：" + getDeviceData(5));
        //电子秤2置零
        NorcoAPIUtil.electronicBalanceSendData(CommonConstants.COM_NODE0, CommonConstants.ZEROORDER2);
        log.info("项目启动电子秤2置零结果：" + getDeviceData(5));*/


        // 复位键按钮（闸机、人脸机、传送带、电子秤复位）释放一次(使用后未释放的情况)
        mNorcoAPI.hwctrl_gpioUnExport(gpioNumber_reset);
        // 导出GPIO
        boolean isSuccess = mNorcoAPI.hwctrl_gpioExport(gpioNumber_reset);
        log.info("122端口复位键导出GPIO isSuccess=" + isSuccess);
        // 配置为输入引脚
        isSuccess = mNorcoAPI.hwctrl_gpioDirection(gpioNumber_reset, "in");
        log.info("122端口复位键配置为输入引脚 isSuccess=" + isSuccess);

        instance.threadPoolTaskExecutor.execute(()->{
            log.info("开始监听gpio122端口复位键：" + gpioNumber_reset);
            int index = 0;
            while (true) {
                try {
                    int reset = mNorcoAPI.hwctrl_gpioRead(gpioNumber_reset);
                    String resetKey = InitBaseInfo.ipAddress + ":gpio:" + gpioNumber_reset;
                    if(reset>0){
                        log.info("复位键123gpio口结果：{}", reset);
                        if(gpio121Map.get(resetKey)==null){
                            gpio121Map.put(resetKey,1);
                        }else {
                            Integer count = gpio121Map.get(resetKey);
                            gpio121Map.put(resetKey, ++count);
                        }
                        index++;
                        //如果按了2秒+，打开或者关闭闸机
                        if(index>=2  && gpio121Map.get(resetKey)!=null && gpio121Map.get(resetKey)>=2){
                            reset();
                            gpio121Map.remove(resetKey);
                            index = 0;
                        }
                    }
                    if(reset == 0 && gpio121Map.get(resetKey)!=null){
                        gpio121Map.remove(resetKey);
                        index = 0;
                    }
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    log.error("睡眠1s异常");
                }

            }
        });

        //以下这行是测试用的，记得删掉
        //testGpio122Reset();

        // 闸机常闭/核验模式切换，释放一次(使用后未释放的情况)
        mNorcoAPI.hwctrl_gpioUnExport(gpioNumber_door);
        // 导出GPIO
        isSuccess = mNorcoAPI.hwctrl_gpioExport(gpioNumber_door);
        log.info("123端口闸机常闭/核验导出GPIO isSuccess=" + isSuccess);
        // 配置为输入引脚
        isSuccess = mNorcoAPI.hwctrl_gpioDirection(gpioNumber_door, "in");
        log.info("123端口闸机常闭/核验配置为输入引脚 isSuccess=" + isSuccess);

        //以下这行是测试用的，记得删掉
        //testDoorGpio123();

        instance.threadPoolTaskExecutor.execute(()->{
            log.info("开始监听闸机常闭/核验gpio123端口：" + gpioNumber_door);
            int index = 0;
            while (true) {
                int door = mNorcoAPI.hwctrl_gpioRead(gpioNumber_door);
                String key = InitBaseInfo.ipAddress + ":gpio:" + gpioNumber_door;
                if(door>0){
                    log.info("闸机常闭/核验123gpio口结果：{}", door);
                    if(gpio121Map.get(key)==null){
                        gpio121Map.put(key,1);
                    }else {
                        Integer count = gpio121Map.get(key);
                        gpio121Map.put(key, ++count);
                    }
                    index++;
                    //如果按了7秒+，打开或者关闭闸机
                    if(index>=7  && gpio121Map.get(key)!=null && gpio121Map.get(key)>=7){
                        pullThings();
                        gpio121Map.remove(key);
                        index = 0;
                    }
                }
                //如果按了2、3秒，打开或者关闭闸机
                if(door == 0 && gpio121Map.get(key)!=null && (gpio121Map.get(key)==2 || gpio121Map.get(key)==3)){
                    doorOpenAndClosed();
                    gpio121Map.remove(key);
                    index = 0;
                }
                //如果是按了4、5、6秒，不做任何操作
                if(door == 0 && gpio121Map.get(key)!=null && (gpio121Map.get(key)==4 || gpio121Map.get(key)==5 || gpio121Map.get(key)==6)){
                    gpio121Map.remove(key);
                    index = 0;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    log.error("睡眠1s异常");
                }
            }
        });

        // 入口尾随红外释放一次(使用后未释放的情况)
        /*mNorcoAPI.hwctrl_gpioUnExport(gpio_tailgating_in);
        // 导出GPIO
        isSuccess = mNorcoAPI.hwctrl_gpioExport(gpio_tailgating_in);
        log.info("in hwctrl_gpioExport isSuccess=" + isSuccess);
        // 配置为输入引脚
        isSuccess = mNorcoAPI.hwctrl_gpioDirection(gpio_tailgating_in, "in");
        log.info("in hwctrl_gpioDirection isSuccess=" + isSuccess);

        instance.threadPoolTaskExecutor.execute(()->{
            log.info("开始监听尾随红外gpio口：" + gpio_tailgating_in);
            int flag = 1;
            while (true) {
                //log.info("尾随红外124端口检测：" + mNorcoAPI.hwctrl_gpioRead(gpio_tailgating_in));
                if(gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:ws:124")!=null &&
                        gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:ws:124")==1){
                    int tailgating = mNorcoAPI.hwctrl_gpioRead(gpio_tailgating_in);
                    String key = InitBaseInfo.ipAddress + ":gpio:" + gpio_tailgating_in;
                    //log.info("尾随红外124端口检测：" + tailgating);
                    //说明有人通过
                    //log.info("尾随红外124端口检测gpiohwMap：" + gpiohwMap);
                    if(tailgating == 0){
                        //log.info("尾随红外124端口检测到有人通过：" + tailgating);
                        if(gpiohwMap.get(key)==null){
                            gpiohwMap.put(key,1);
                        }else {
                            if(flag == 2){
                                Integer count = gpiohwMap.get(key);
                                gpiohwMap.put(key, ++count);
                            }
                        }
                        flag = 1;
                    }

                    //没有人通过
                    if(gpiohwMap.get(key)!=null && tailgating == 1){
                        flag = 2;
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(50);
                    } catch (InterruptedException e) {
                        log.error("睡眠50ms异常");
                    }
                }else {
                    try {
                        TimeUnit.MILLISECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        log.error("睡眠100ms异常");
                    }
                }
            }
        });


        // 出口尾随红外释放一次(使用后未释放的情况)
        mNorcoAPI.hwctrl_gpioUnExport(gpio_tailgating_out);
        // 导出GPIO
        isSuccess = mNorcoAPI.hwctrl_gpioExport(gpio_tailgating_out);
        log.info("in hwctrl_gpioExport isSuccess=" + isSuccess);
        // 配置为输入引脚
        isSuccess = mNorcoAPI.hwctrl_gpioDirection(gpio_tailgating_out, "in");
        log.info("in hwctrl_gpioDirection isSuccess=" + isSuccess);

        instance.threadPoolTaskExecutor.execute(()->{
            log.info("开始监听尾随红外gpio口：" + gpio_tailgating_out);
            int flag = 1;
            while (true) {
                //log.info("尾随红外125端口检测：" + mNorcoAPI.hwctrl_gpioRead(gpio_tailgating_out));
                if(gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:ws:125")!=null &&
                        gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:ws:125")==1){
                    int tailgating = mNorcoAPI.hwctrl_gpioRead(gpio_tailgating_out);
                    String key = InitBaseInfo.ipAddress + ":gpio:" + gpio_tailgating_out;
                    //log.info("尾随红外125端口检测：" + tailgating);
                    //说明有人通过
                    //log.info("尾随红外125端口检测gpiohwMap：" + gpiohwMap);
                    if(tailgating == 0){
                        //log.info("尾随红外125端口检测到有人通过：" + tailgating);
                        if(gpiohwMap.get(key)==null){
                            gpiohwMap.put(key,1);
                        }else {
                            if(flag == 2){
                                Integer count = gpiohwMap.get(key);
                                gpiohwMap.put(key, ++count);
                            }
                        }
                        flag = 1;
                    }

                    //没有人通过
                    if(gpiohwMap.get(key)!=null && tailgating == 1){
                        flag = 2;
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(50);
                    } catch (InterruptedException e) {
                        log.error("睡眠50ms异常");
                    }
                }else {
                    try {
                        TimeUnit.MILLISECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        log.error("睡眠100ms异常");
                    }
                }
            }
        });*/


        // 释放一次(使用后未释放的情况)
        //金属探测门
        mNorcoAPI.hwctrl_gpioUnExport(gpioNumber_metal_detect);
        // 导出GPIO
        isSuccess = mNorcoAPI.hwctrl_gpioExport(gpioNumber_metal_detect);
        log.info("监听金属探测门122端口 in hwctrl_gpioExport isSuccess=" + isSuccess);
        // 配置为输入引脚
        isSuccess = mNorcoAPI.hwctrl_gpioDirection(gpioNumber_metal_detect, "in");
        log.info("设置监听金属探测门122端口为输入 in hwctrl_gpioDirection isSuccess=" + isSuccess);

        //以下这行是测试用的，记得删掉
        //testMetalGpio121();

    }

    private void reset() throws InterruptedException {
        log.info("复位命令开始");
        log.info("电子秤置零开始");
        String zeroorder = CommonConstants.ZEROORDER1_1;
        if(electronicBalanceIndex == 2){
            zeroorder = CommonConstants.ZEROORDER2_1;
        }
        NorcoAPIUtil.electronicBalanceSendData(CommonConstants.COM_NODE0, zeroorder);
        log.info("电子秤1置零结果：{}",getDeviceData(5));

        zeroorder = CommonConstants.ZEROORDER1_2;
        if(electronicBalanceIndex == 2){
            zeroorder = CommonConstants.ZEROORDER2_2;
        }
        NorcoAPIUtil.electronicBalanceSendData(CommonConstants.COM_NODE0, zeroorder);
        log.info("电子秤置2零结果：{}",getDeviceData(5));
        log.info("电子秤置零结束");

        //闸机复位（改成核验模式）
        GatePassSetVo gatePassSetVo = GuavaCache.PASSSET_CACHE.getIfPresent("passsetipInfo");
        String gateAIp = gatePassSetVo.getInGateIp();//A门闸机IP
        String gateBIp = gatePassSetVo.getOutGateIp();//B门闸机IP
        Socket doorA = null;
        Socket doorB = null;

        try {
            doorA = new Socket(gateAIp, gateAPort);
            doorB = new Socket(gateBIp, gateBPort);
            String key = RedisKeyEnum.KEY_REMOTE_CONTROLL.getCode();
            log.info("闸机开启核验模式开始");
            String isSuccess_A = SocketClientUtil.sendData(doorA, doorCommand3);
            log.info("A门闸机开启核验模式:" + isSuccess_A);
            String isSuccess_B = SocketClientUtil.sendData(doorB, doorCommand3);
            log.info("B门闸机开启核验模式:" + isSuccess_B);
            DOOR_MODE_CACHE.put(key, 1);
            log.info("闸机开启核验模式结束");
        } catch (IOException e) {
            log.error("复位键闸机开启核验异常：{}",e.getMessage());
        }finally {
            try {
                if (doorA != null) {
                    doorA.close();
                }
                if (doorB != null) {
                    doorB.close();
                }
            } catch (IOException e) {
                log.error("复位键闸机开启核验异常：{}",e.getMessage());
            }
        }

        Socket conveyer = null;
        try {
            //传送带复位（改成核验模式）
            log.info("传送带复位命令开始");
            conveyer = new Socket(gatePassSetVo.getPclIp(), 502);
            String str = SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M6.getCode());
            log.info("传送带复位命令结束：[{}]",str);
        } catch (Exception ex) {
            log.error("重置传送带复位报错：[{}]", ex.getMessage(), ex);
        }finally {
            try {
                if(conveyer != null){
                    conveyer.close();
                }
            } catch (IOException e) {
                log.error("关闭传送带报错：[{}]", e.getMessage(), e);
            }
        }

        //复位人脸机
        log.info("给人脸机发送解锁命令开始");
        //A门人脸机IP
        String faceAIp = gatePassSetVo.getInFaceIp();
        //B门人脸机IP
        String faceBIp = gatePassSetVo.getOutFaceIp();
        FaceServerHandler.sendFace(FaceServerHandler.channels.get(faceBIp), UUID.fastUUID().toString().replace("-", ""), "on","人脸机复位");
        FaceServerHandler.sendFace(FaceServerHandler.channels.get(faceAIp), UUID.fastUUID().toString().replace("-", ""), "on","人脸机复位");

        log.info("给人脸机发送解锁命令结束");

        //10s（重置完传送带）后电子秤清零
        //TimeUnit.SECONDS.sleep(10);

        log.info("复位命令结束");
    }


    private void testGpio122Reset() {

        instance.threadPoolTaskExecutor.execute(()->{
            while (true) {
                int reset = mNorcoAPI.hwctrl_gpioRead(gpioNumber_reset);
                log.info("复位键122gpio口结果：{}", reset);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    log.error("睡眠1s异常");
                }
            }
        });
    }

    private void testDoorGpio123() {
        instance.threadPoolTaskExecutor.execute(()->{
            while (true) {
                log.info("闸机常闭/核验123gpio口结果：{}", mNorcoAPI.hwctrl_gpioRead(gpioNumber_door));
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    log.error("睡眠1s异常");
                }
            }
        });
    }

    private static void testMetalGpio121() {
        instance.threadPoolTaskExecutor.execute(()->{
            while(true){
                int metal_detect = mNorcoAPI.hwctrl_gpioRead(gpioNumber_metal_detect);
                log.info("金属门121端口检测：" + metal_detect);
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("睡眠1s异常");
                }
            }
        });
    }

    private void pullThings() {
        Socket conveyer = null;
        try {
            //左进右出0->1->2/3   右进左出4->5->2/3
            //0->1/7    4->5/8
            GatePassSetVo gatePassSetVo = GuavaCache.PASSSET_CACHE.getIfPresent("passsetipInfo");
            conveyer = new Socket(gatePassSetVo.getPclIp(), 502);
            SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M9.getCode());
        } catch (IOException e) {
            log.error("gpio121端口5s+取物品异常：{}",e.getMessage());
        }finally {
            try {
                if(conveyer != null){
                    conveyer.close();
                }
            } catch (IOException e) {
                log.error("gpio121端口5s+取物品异常：{}",e.getMessage());
            }
        }
    }

    private void doorOpenAndClosed() {
        GatePassSetVo gatePassSetVo = GuavaCache.PASSSET_CACHE.getIfPresent("passsetipInfo");
        String gateAIp = gatePassSetVo.getInGateIp();//A门闸机IP
        String gateBIp = gatePassSetVo.getOutGateIp();//B门闸机IP
        Socket doorA = null;
        Socket doorB = null;

        try {
            doorA = new Socket(gateAIp, gateAPort);
            doorB = new Socket(gateBIp, gateBPort);
            String key = RedisKeyEnum.KEY_REMOTE_CONTROLL.getCode();
            Integer workMode = DOOR_MODE_CACHE.getIfPresent(key);
            if (workMode == 1) {//说明当前是核验，需要改成常开模式
                String isSuccess_A = SocketClientUtil.sendData(doorA, doorCommand1);
                log.info("A门闸机常开:" + isSuccess_A);
                String isSuccess_B =SocketClientUtil.sendData(doorB, doorCommand1);
                log.info("B门闸机常开:" + isSuccess_B);
                DOOR_MODE_CACHE.put(key, 2);
            }else{//说明当前是常开，需要改成核验模式
                log.info("开启核验模式");
                String isSuccess_A = SocketClientUtil.sendData(doorA, doorCommand3);
                log.info("A门闸机开启核验模式:" + isSuccess_A);
                String isSuccess_B = SocketClientUtil.sendData(doorB, doorCommand3);
                log.info("B门闸机开启核验模式:" + isSuccess_B);
                DOOR_MODE_CACHE.put(key, 1);
            }
        } catch (IOException e) {
            log.error("打开或者关闭闸机异常：{}",e.getMessage());
        }finally {
            try {
                if (doorA != null) {
                    doorA.close();
                }
                if (doorB != null) {
                    doorB.close();
                }
            } catch (IOException e) {
                log.error("打开或者关闭闸机异常：{}",e.getMessage());
            }
        }


        //复位人脸机
        log.info("给人脸机发送解锁命令开始");
        //A门人脸机IP
        String faceAIp = gatePassSetVo.getInFaceIp();
        //B门人脸机IP
        String faceBIp = gatePassSetVo.getOutFaceIp();

        FaceServerHandler.sendFace(FaceServerHandler.channels.get(faceBIp), UUID.fastUUID().toString().replace("-", ""), "on","人脸机复位");
        FaceServerHandler.sendFace(FaceServerHandler.channels.get(faceAIp), UUID.fastUUID().toString().replace("-", ""), "on","人脸机复位");

        log.info("给人脸机发送解锁命令结束");

    }

    // 扫码串口数据回调接口
    private static UartReceiveDataCallback mDataCallbackScanCode = NorcoAPIUtil::displayScanCodeReceiveData;

    // 电子秤串口数据回调接口
    private static UartReceiveDataCallback mDataCallbackElectronicBalance = NorcoAPIUtil::displayElectronicBalanceReceiveData;

    /**
     * 电子秤发生称重命令
     * @param node   端口
     * @param order 电子秤指令，包含称重和置零两种
     */
    public static void electronicBalanceSendData(String node, String order) {
        instance.threadPoolTaskExecutor.execute(new AutoSendThread(node, order));
    }

    // 发送测试
    private static class AutoSendThread extends Thread {

        private String node;
        //电子秤指令，包含称重和置零两种
        private String order;


        public AutoSendThread(String node, String order) {
            this.node = node;
            this.order = order;
        }

        @Override
        public void run() {
            //置零置零只需要执行一次
            if(CommonConstants.ZEROORDER1_1.equals(order) || CommonConstants.ZEROORDER1_2.equals(order)
                ||CommonConstants.ZEROORDER2_1.equals(order) || CommonConstants.ZEROORDER2_2.equals(order)
                ||CommonConstants.ZEROADJUST1.equals(order) || CommonConstants.ZEROADJUST2.equals(order)){
                sendData(node, order, true);
            }else {
                long startTime = System.currentTimeMillis();
                while(System.currentTimeMillis()-startTime < weightTime)
                {
                    sendData(node, order, true);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    // 发送数据
    private static void sendData(String node, String data, boolean isHex) {
        byte[] sendByte = null;
        if (isHex) {
            sendByte = ByteUtil.hexToByteArr(data);
        } else {
            sendByte = data.getBytes();
        }
        if (mNorcoAPI.hwctrl_uartOpened(node)) {
            mNorcoAPI.hwctrl_uartSend(node, sendByte);
        }
    }

    /**
     * 扫码接收数据
     * @param bean
     */
    private static void displayScanCodeReceiveData(ReceiveDataBean bean) {
        try {
            if(CommonConstants.COM_NODE1.equals(bean.mNode)){
                mobileCode = new String(bean.mReceiveData, CharsetUtil.UTF_8);
                mobileCode = mobileCode.replaceAll("\\s*|\t|\r|\n","");
                if(StringUtils.isNotBlank(mobileCode)&&mobileCode.contains("序列号")){
                    mobileCode = mobileCode.substring(mobileCode.indexOf("序列号:") + 4,mobileCode.indexOf("跳码:"));
                }
                try {
                    MdmMobileVo mdmMobileVo = JSON.parseObject(AESUtil.decrypt(mobileCode), MdmMobileVo.class);
                    mobileCode = mdmMobileVo.getEmployeeID();
                } catch (Exception e) {

                }

                log.info("手机二维码扫描结果：{}", mobileCode);
            }
            if(CommonConstants.COM_NODE2.equals(bean.mNode)){
                tokenCode = new String(bean.mReceiveData, CharsetUtil.UTF_8);
                tokenCode = tokenCode.replaceAll("\\s*|\t|\r|\n","");
                log.info("token二维码扫描结果：{}", tokenCode);
            }
            if(CommonConstants.COM_NODE3.equals(bean.mNode)){
                mobileCode2 = new String(bean.mReceiveData, CharsetUtil.UTF_8);
                mobileCode2 = mobileCode2.replaceAll("\\s*|\t|\r|\n","");
                if(StringUtils.isNotBlank(mobileCode2)&&mobileCode2.contains("序列号")){
                    mobileCode2 = mobileCode2.substring(mobileCode2.indexOf("序列号:") + 4,mobileCode2.indexOf("跳码:"));
                }
                try {
                    MdmMobileVo mdmMobileVo = JSON.parseObject(AESUtil.decrypt(mobileCode2), MdmMobileVo.class);
                    mobileCode2 = mdmMobileVo.getEmployeeID();
                } catch (Exception e) {

                }
                log.info("手机二维码扫描结果2：{}", mobileCode2);
            }
            if(CommonConstants.COM_NODE4.equals(bean.mNode)){
                tokenCode2 = new String(bean.mReceiveData, CharsetUtil.UTF_8);
                tokenCode2 = tokenCode2.replaceAll("\\s*|\t|\r|\n","");
                log.info("token二维码扫描结果2：{}", tokenCode2);
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据电子秤设备返回的信息，获取重量，单位为克(g)
     * @param bean
     */
    private static void displayElectronicBalanceReceiveData(ReceiveDataBean bean) {
        //称重正常返回数据为：010404000000753A63，只需要取00000075，舍去前面六位，后面四位
        String msg = ByteUtil.byteArrToHex(bean.mReceiveData);

        //新电子秤是34位（1kg为：01310251302B303031303030366B670372，第第二位为B则是正数，为D则是负数，30303130303036 这一串取偶次的字符串（0010006），最后一位为10幂次方即公式为：0.001000 * 10的6次方）
        if(StringUtils.isNotBlank(msg) && msg.length() == 34){
            //电子秤1
            if(msg.startsWith("0131")){

                log.info("电子秤1称重结果：" + msg);

                String start = msg.substring(11,12);
                msg = msg.substring(12,26);
                StringBuilder evenChars = new StringBuilder();
                for (int i = 0;i<msg.length();i++) {
                    if (i % 2 == 1) {
                        evenChars.append(msg.charAt(i));
                    }
                }
                msg = evenChars.toString();
                Double result = Double.parseDouble("0." + msg.substring(0, msg.length()-1)) * Math.pow(10, Double.parseDouble(msg.substring(msg.length()-1)));
                if(start.startsWith("B")){//重量为正数
                    weightSet1.add(new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue());
                }

                if(start.startsWith("D")){//重量为负数
                    result = Double.parseDouble("-" + result);
                    weightSet1.add(new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue());
                }
            }
            //电子秤2
            if(msg.startsWith("0132")){

                log.info("电子秤2称重结果：" + msg);

                String start = msg.substring(11,12);
                msg = msg.substring(12,26);
                StringBuilder evenChars = new StringBuilder();
                for (int i = 0;i<msg.length();i++) {
                    if (i % 2 == 1) {
                        evenChars.append(msg.charAt(i));
                    }
                }
                msg = evenChars.toString();
                Double result = Double.parseDouble("0." + msg.substring(0, msg.length()-1)) * Math.pow(10, Double.parseDouble(msg.substring(msg.length()-1)));
                if(start.startsWith("B")){//重量为正数
                    weightSet2.add(new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue());
                }

                if(start.startsWith("D")){//重量为负数
                    result = Double.parseDouble("-" + result);
                    weightSet2.add(new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue());
                }
            }
        }
        ////老称（暂时没用了）是18位
        if(StringUtils.isNotBlank(msg) && msg.length() == 18){
            //电子秤1
            if(msg.startsWith("01")){

                log.info("电子秤1称重结果：" + msg);

                msg = msg.substring(6,msg.length() - 4);

                if(!msg.startsWith("FF")){//重量为正数
                    weightSet1.add((double) Integer.parseInt(msg, 16));
                }

                if(msg.startsWith("FF")){//重量为负数
                    byte[] bytes = ByteUtil.hexToByteArr(msg);
                    byte temp;
                    for (int i = 0; i < bytes.length; i++) {
                        temp = bytes[i];
                        bytes[i] = (byte) ~temp;
                    }
                    String ByteArr = ByteUtil.byteArrToHex(bytes);
                    weightSet1.add((double) -(Integer.parseInt(ByteArr,16) + 1));
                }
            }
            //电子秤2
            if(msg.startsWith("02")){

                log.info("电子秤2称重结果：" + msg);

                msg = msg.substring(6,msg.length() - 4);

                if(!msg.startsWith("FF")){//重量为正数
                    weightSet2.add((double) Integer.parseInt(msg, 16));
                }

                if(msg.startsWith("FF")){//重量为负数
                    byte[] bytes = ByteUtil.hexToByteArr(msg);
                    byte temp;
                    for (int i = 0; i < bytes.length; i++) {
                        temp = bytes[i];
                        bytes[i] = (byte) ~temp;
                    }
                    String ByteArr = ByteUtil.byteArrToHex(bytes);
                    weightSet2.add((double) -(Integer.parseInt(ByteArr,16) + 1));
                }
            }


        }
        //新电子秤
        if(StringUtils.isNotBlank(msg) && msg.length() == 12){//置零指令为12位，结果为：013102510372/013202510372
            log.info("电子秤置零/校准结果：" + msg);
            weightZero = msg;
        }
        ////老称（暂时没用了）
        if(StringUtils.isNotBlank(msg) && msg.length() == 16){//置零指令为16位
            log.info("电子秤置零/校准结果：" + msg);
            weightZero = msg;
        }
    }

    /**
     *
     * @param type 1:获取手机二维码，2：获取token二维码，3:获取手机2二维码，4：获取token2二维码，5：获取电子秤归零/零点标定结果
     * @return
     * @throws InterruptedException
     */
    public static String getDeviceData(int type) throws InterruptedException {
        int count = 1;
        String result = "";
        switch (type) {
            case 1:
            while (StrUtil.isEmpty(NorcoAPIUtil.mobileCode)){
                if(count>10){
                    break;
                }
                TimeUnit.MILLISECONDS.sleep(300);
                count++;
            }
            //数据用完就需要置为默认值
            result = NorcoAPIUtil.mobileCode;
            NorcoAPIUtil.mobileCode = "";
            break;
            case 2:
                while (StrUtil.isEmpty(NorcoAPIUtil.tokenCode)){
                    if(count>10){
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(300);
                    count++;
                }
                //数据用完就需要置为默认值
                result = NorcoAPIUtil.tokenCode;
                NorcoAPIUtil.tokenCode = "";
                break;
            case 3:
                while (StrUtil.isEmpty(NorcoAPIUtil.mobileCode2)){
                    if(count>10){
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(300);
                    count++;
                }
                //数据用完就需要置为默认值
                result = NorcoAPIUtil.mobileCode2;
                NorcoAPIUtil.mobileCode2 = "";
                break;
            case 4:
                while (StrUtil.isEmpty(NorcoAPIUtil.tokenCode2)){
                    if(count>10){
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(300);
                    count++;
                }
                //数据用完就需要置为默认值
                result = NorcoAPIUtil.tokenCode2;
                NorcoAPIUtil.tokenCode2 = "";
                break;
            case 5:
                while (StrUtil.isEmpty(NorcoAPIUtil.weightZero)){
                    if(count>10){
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(200);
                    count++;
                }
                //数据用完就需要置为默认值
                result = NorcoAPIUtil.weightZero;
                NorcoAPIUtil.weightZero = "";
                break;
        }

        return result;
    }

    /**
     * @param type 1:电子秤1的返回结果，2：电子秤2的返回结果
     * 获取电子秤的数据
     * @return
     * @throws InterruptedException
     */
    public static Set<Double> getDeviceWeightData(int type) {
        try {
            //获取重量前，先把手机二维码、token二维码置空，以免影响此次的扫码的结果(如果此次因为遮挡，导致扫码失败，但是上次的二维码还有值的情况下)
            NorcoAPIUtil.mobileCode = "";
            NorcoAPIUtil.tokenCode = "";
            NorcoAPIUtil.mobileCode2 = "";
            NorcoAPIUtil.tokenCode2 = "";
            int count = 1;

            if(type == 1){
                String weightorder = CommonConstants.WEIGHTORDER1_1;
                if(electronicBalanceIndex == 2){
                    weightorder = CommonConstants.WEIGHTORDER2_1;
                }
                NorcoAPIUtil.electronicBalanceSendData(CommonConstants.COM_NODE0, weightorder);
                //电子秤连续称重期间，进行睡眠处理，比如需要连续称重2s中，那么睡眠2s+200ms
                TimeUnit.MILLISECONDS.sleep(new Double(weightTime).longValue()  + 300);
                while (CollectionUtil.isEmpty(NorcoAPIUtil.weightSet1)) {
                    if (count > 10) {
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(200);
                    count++;
                }
                //数据用完就需要置为默认值
                log.info("电子秤1的值：{}", NorcoAPIUtil.weightSet1);
                Set<Double> result = NorcoAPIUtil.weightSet1.stream().filter(s -> s >= weightError).sorted(Double::compareTo).collect(Collectors.toSet());
                NorcoAPIUtil.weightSet1.clear();
                return result;
            }

            if(type == 2){
                String weightorder = CommonConstants.WEIGHTORDER1_2;
                if(electronicBalanceIndex == 2){
                    weightorder = CommonConstants.WEIGHTORDER2_2;
                }
                electronicBalanceSendData(CommonConstants.COM_NODE0, weightorder);
                //电子秤连续称重期间，进行睡眠处理，比如需要连续称重2s中，那么睡眠2s+200ms
                TimeUnit.MILLISECONDS.sleep(new Double(weightTime).longValue() + 300);
                while (CollectionUtil.isEmpty(NorcoAPIUtil.weightSet2)) {
                    if (count > 10) {
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(200);
                    count++;
                }
                //数据用完就需要置为默认值
                log.info("电子秤2的值：{}", NorcoAPIUtil.weightSet2);
                Set<Double> result = NorcoAPIUtil.weightSet2.stream().filter(s -> s > weightError).sorted(Double::compareTo).collect(Collectors.toSet());
                NorcoAPIUtil.weightSet2.clear();
                return result;
            }
        } catch (Exception e) {
            log.error("称重报错：{}", e.getMessage());
        }

        return null;
    }
}
